package com.zxl.algorithm;

import com.zxl.entity.Point;

import java.security.MessageDigest;
import java.util.*;

/**
 * @author zxl
 * @version 1.0
 * @description 唯一值算法，用于生成唯一的标识符
 * @date 2025/7/18 23:09
 */
public class UniqueValueAlgorithm {

    /** UUID v5 命名空间方案 */
    public static final String UUID_KEY = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";

    /**
     * SHA-256截断 + Base64编码（28字符）
     * 输出结果类似：ZKYmQeNtRqW4gT7wEgj3AA
     */
    public static String generateCompactHash(List<Point> points) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            List<Point> sortedPoints = getSortedPoints(points);

            for (Point p : sortedPoints) {
                byte[] lngBytes = longToBytes(Double.doubleToLongBits(p.getLongitude()));
                byte[] latBytes = longToBytes(Double.doubleToLongBits(p.getLatitude()));
                digest.update(lngBytes);
                digest.update(latBytes);
            }

            byte[] fullHash = digest.digest();
            System.out.println(fullHash.length);
            System.out.println(UUID.nameUUIDFromBytes(fullHash).toString());
            // 如果需要完整的SHA-256算法的结果，将fullHash转换为十六进制字符串返回即可
            // return bytesToHex(fullHash);
            // 这里我们只取前16字节进行Base64编码，以减少长度
//            byte[] truncated = new byte[16];
//            System.arraycopy(fullHash, 0, truncated, 0, 16);
//            return Base64.getUrlEncoder().withoutPadding().encodeToString(truncated);
            return UUID.nameUUIDFromBytes(fullHash).toString().replaceAll("-", "");

        } catch (Exception e) {
            throw new RuntimeException("生成Hash唯一索引失败", e);
        }
    }

    /**
     * UUID v5 命名空间方案（36字符）
     * @param points
     * @return
     */
    public static String generateUUID(List<Point> points) {
        try {
            // 命名空间，可以使用任何UUID，为了保证每次生成的值一样，需要使用固定的值
            UUID namespace = UUID.fromString(UUID_KEY);
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");

            // 添加命名空间 （主要用于系统或者业务隔离，也可以降低单系统的碰撞率）
//            sha1.update(longToBytes(namespace.getMostSignificantBits()));
//            sha1.update(longToBytes(namespace.getLeastSignificantBits()));

            // 添加排序后的点数据
            for (Point p : points) {
                // Double.doubleToLongBits 方法不受精度影响
                // 如果使用p.getLongitude() * 10000 这种首先小数位数不确定，其次即使确定也可能丢失精度
                byte[] lngBytes = longToBytes(Double.doubleToLongBits(p.getLongitude()));
                byte[] latBytes = longToBytes(Double.doubleToLongBits(p.getLatitude()));
                sha1.update(lngBytes);
                sha1.update(latBytes);
            }

            byte[] hash = sha1.digest();

            return UUID.nameUUIDFromBytes(hash).toString().replaceAll("-", "");
        } catch (Exception e) {
            throw new RuntimeException("UUIDv5 生成失败", e);
        }
    }

    /**
     * 排序方法，且对原始数据不影响
     * @param points
     * @return
     */
    private static List<Point> getSortedPoints(List<Point> points) {
        // new 一个新的，防止原始数据被修改
        List<Point> sorted = new ArrayList<>(points);
        Collections.sort(sorted, Comparator.comparingDouble(Point::getLongitude)
                .thenComparingDouble(Point::getLatitude));
        return sorted;
    }

    /**
     * long转字节数组
     * @param l
     * @return
     */
    private static byte[] longToBytes(long l) {
        byte[] result = new byte[8];
        for (int i = 7; i >= 0; i--) {
            result[i] = (byte)(l & 0xFF);
            l >>= 8;
        }
        return result;
    }

    /**
     * 字节数组转十六进制
     * @param bytes
     * @return
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if(hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static void main(String[] args) {
        List<Point> list = new ArrayList<>();
        list.add(new Point(113.09,23.21));
        list.add(new Point(113.28,23.21));
        list.add(new Point(113.28,23.08));
        list.add(new Point(113.09,23.08));
        list.add(new Point(113.11,23.08));

        System.out.println(generateUUID(list));
        System.out.println(generateCompactHash(list));
        System.out.println(Double.doubleToLongBits(126.3256));

    }
}
